package com.zaimokuza.cn.service.aspect;

import com.alibaba.fastjson.JSON;
import com.zaimokuza.cn.entity.log.RequestLog;
import com.zaimokuza.cn.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 请求日志切面
 *
 * @author zaimokuza
 */
@Slf4j
@Aspect
@Component
public class RequestLogAspect {

    @Resource
    private LogService logService;

    @Pointcut("execution(public * com.zaimokuza.cn.controller..*.*(..))")
    public void requestPoint() {
    }

    @Around("requestPoint()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 记录开始时刻
        long startTime = System.currentTimeMillis();
        // 实例请求日志对象
        RequestLog requestLog = new RequestLog();
        // 请求时间
        requestLog.setRequestTime(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
        // 获取请求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 请求地址
        requestLog.setUrl(request.getRequestURI());
        // 请求方式
        requestLog.setHttpMethod(request.getMethod());
        // IP地址
        requestLog.setRemoteIp(getIpAddr(request));
        // 获取请求的类名
        requestLog.setClassName(proceedingJoinPoint.getTarget().getClass().getName());
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        // 获取切入点所在的方法名
        requestLog.setMethodName(signature.getMethod().getName());
        // 请求参数
        boolean hasMultipartFile = false;
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof MultipartFile) {
                hasMultipartFile = true;
                break;
            }
        }
        requestLog.setParams(hasMultipartFile ? "" : JSON.toJSONString(proceedingJoinPoint.getArgs()));
        try {
            // 执行
            Object result = proceedingJoinPoint.proceed();
            // 状态标识
            requestLog.setStatus(true);
            // 返回结果
            return result;
        } catch (Throwable throwable) {
            // 状态标识
            requestLog.setStatus(false);
            // 异常信息
            requestLog.setErrorMsg(throwable.getMessage());
            // 返回
            throw throwable;
        } finally {
            // 总耗时
            requestLog.setTotalMs(System.currentTimeMillis() - startTime);
            // 打印日志
            log.info(requestLog.toString());
            // 记录日志，方法标记为异步调用，不会和业务数据一起回滚
            logService.saveRequestLog(requestLog);
        }
    }

    /**
     * 获取登录用户远程主机ip地址
     *
     * @param request 请求
     * @return ip地址
     */
    private String getIpAddr(HttpServletRequest request) throws UnsupportedEncodingException {
        // 获取前端传递的IP地址及城市名称
        String addressIp = request.getHeader("address-ip");
        String addressCity = request.getHeader("address-city");
        // 判定前端是否有获取到IP信息
        if (StringUtils.isNotBlank(addressIp) && StringUtils.isNotBlank(addressCity)) {
            // 前端获取到IP信息对该信息进行解码
            addressIp = URLDecoder.decode(addressIp, "UTF-8");
            addressCity = URLDecoder.decode(addressCity, "UTF-8");
            log.info("IP:{},{}" + addressIp, addressCity);
            return addressIp;
        } else {
            // 前端没有获取到时尝试从request获取
            return request.getRemoteAddr();
        }
    }


}
